package co.edu.uniandes.ecos.itera.tsp.mundo;

import co.edu.uniandes.ecos.itera.tsp.accesodatos.DefectoDAL;
import co.edu.uniandes.ecos.itera.tsp.accesodatos.InterrupcionDAL;
import co.edu.uniandes.ecos.itera.tsp.accesodatos.ProyectoDAL;
import co.edu.uniandes.ecos.itera.tsp.anotaciones.LogInt;
import co.edu.uniandes.ecos.itera.tsp.anotaciones.LogT;
import co.edu.uniandes.ecos.itera.tsp.anotaciones.Loc;
import co.edu.uniandes.ecos.itera.tsp.anotaciones.Tiempos;
import co.edu.uniandes.ecos.itera.tsp.anotaciones.LogD;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Clase que expone la funcionalidad de este paquete a los demoas paquetes que
 * la deseen usar
 */
@LogT(date = "2011-03-19", min = 50, taskId = 73)
public class DelegadoNegocio {
	/**
	 * Listado de proyectos que se trabajan en el sistema
	 */
	private List<Proyecto> proyectos;

	/**
	 * Listado de defectos que se trabajan en el sistema
	 */
	private List<Defecto> defectos;

	/**
	 * Pisubles interrupciones contempladas en el sistema
	 */
	private List<Interrupcion> interrupciones;

	/**
	 * Errores encontrados durante la carga
	 */
	private List<String> erroresDuranteCarga;

	/**
	 * Constructor por defecto de esta clase
	 */
	public DelegadoNegocio() {
		this.proyectos = new ArrayList<Proyecto>();
		this.defectos = new ArrayList<Defecto>();
		this.interrupciones = new ArrayList<Interrupcion>();
	}

	/**
	 * Agrega un proyecto al grupo de proyectos del sistema
	 * 
	 * @param proyecto
	 *            Proyecto a agregar
	 */
	@Loc(size = 1, taskId = 73)
	public void agregarProyecto(Proyecto proyecto) {
		this.proyectos.add(proyecto);
	}

	/**
	 * Lista los proyectos manejados en este sistema
	 * 
	 * @return Proyectos que se manejan en el sistema
	 */
	@Loc(size = 1, taskId = 73)
	public List<Proyecto> listarProyectos() {
		return this.proyectos;
	}

	/**
	 * Agrega un defecto al listado de defectos manejados en el sistema
	 * 
	 * @param defecto
	 *            Defecto a agregar
	 */
	@Loc(size = 1, taskId = 73)
	public void agregarDefecto(Defecto defecto) {
		this.defectos.add(defecto);
	}

	/**
	 * Lista los defectos del sistema
	 * 
	 * @return Defectos manejados en el sistema
	 */
	@Loc(size = 1, taskId = 73)
	public List<Defecto> listarDefectos() {
		return this.defectos;
	}

	/**
	 * Agrega una interrupcion al sistema
	 * 
	 * @param interrupcion
	 *            Interrupcion a agregar
	 */
	@Loc(size = 1, taskId = 73)
	public void agregarInterrupcion(Interrupcion interrupcion) {
		this.interrupciones.add(interrupcion);
	}

	/**
	 * Lista las interrupciones del sistema
	 * 
	 * @return Interrupciones manejadas en el sistema
	 */
	@Loc(size = 1, taskId = 73)
	public List<Interrupcion> listarInterrupciones() {
		return this.interrupciones;
	}

	/**
	 * Busca una interrupcion en el listado de interrupciones
	 * 
	 * @param idInterrupcion
	 *            ID de la interrupcion a buscar
	 * @return Interrupcion encontrada. NULL si no la encuentra
	 */
	@Loc(size = 10, taskId = 73)
	private Interrupcion encontrarInterrupcion(int idInterrupcion) {
		Interrupcion interrupcionEncontrada = null;

		for (Interrupcion interrupcion : this.interrupciones) {
			if (interrupcion.getIntId() == idInterrupcion) {
				interrupcionEncontrada = interrupcion;
				break;
			}
		}

		return interrupcionEncontrada;
	}

	/**
	 * Busca un defecto dentro del listado de defectos del sistema
	 * 
	 * @param idDefecto
	 *            Id del defecto a buscar
	 * @return Defecto encontrado. NULL si no lo encuentra
	 */
	@Loc(size = 10, taskId = 73)
	private Defecto encontrarDefecto(int idDefecto) {
		Defecto defectoEncontrado = null;

		for (Defecto defecto : this.defectos) {
			if (defecto.getIdDefecto() == idDefecto) {
				defectoEncontrado = defecto;
				break;
			}
		}

		return defectoEncontrado;
	}

	/**
	 * Busca una tarea entre todos los proyectos
	 * 
	 * @param idTarea
	 *            Id de la tarea a buscar
	 * @return Tarea encontrada. En caso de no encontrarla retorna NULL
	 */
	@Loc(size = 20, taskId = 73)
	private Tarea encontrarTarea(int idTarea) {
		Tarea tareaEncontrada = null;

		for (Proyecto proyecto : this.proyectos) {
			for (Ciclo ciclo : proyecto.listarCiclos()) {
				for (Tarea tarea : ciclo.listarTareas()) {
					if (tarea.getCodigo() == idTarea) {
						tareaEncontrada = tarea;
						break;
					}
				}

				if (tareaEncontrada != null)
					break;
			}

			if (tareaEncontrada != null)
				break;
		}

		return tareaEncontrada;
	}

	/**
	 * Adiciona lineas de codigo a una tarea
	 * 
	 * @param locs
	 *            Lineas a agregar
	 * @param idTarea
	 *            Id de la tarea ala cual se le van a agregar las lineas de
	 *            codigo
	 * @throws Exception
	 *             En caso de no poder encontrar la tarea genera una excepcion
	 */
	@Loc(size = 4, taskId = 73)
	void adicionarLOCsATarea(int locs, int idTarea) throws Exception {
		Tarea tarea = this.encontrarTarea(idTarea);
		if (tarea == null)
			throw new Exception("Tarea no encontrada. ID=" + idTarea);

		tarea.adicionarLOCs(locs);
	}

	/**
	 * Adiciona tiempo de trabajo a una tarea
	 * 
	 * @param minutos
	 *            Minutos de ejecucion a agregar a una tarea
	 * @param idTarea
	 *            ID de la tarea a la cual se le van a agregar los minutos de
	 *            trabajo
	 * @param fecha
	 *            Fecha en donde se trabajaron dichos minutos para esta tarea
	 * @throws Exception
	 *             En caso de no poder encontrar la tarea genera una excepcion
	 */
	@Loc(size = 4, taskId = 73)
	void adicionarTiempoATarea(int minutos, int idTarea, String fecha)
			throws Exception {
		Tarea tarea = this.encontrarTarea(idTarea);
		if (tarea == null)
			throw new Exception("Tarea no encontrada. ID=" + idTarea);

		tarea.adicionarTiempoReal(minutos);
	}

	/**
	 * Adiciona una interrupcion a una tarea
	 * 
	 * @param idInterrupcion
	 *            Id de la interrupcion a agregar
	 * @param idTarea
	 *            Id de la tarea a agregar
	 * @param minutos
	 *            Minutos que duro esta interrupcion
	 * @param fecha
	 *            Fecha en la que ocurrio esta interrupcion
	 * @throws Exception
	 *             En caso de no poder encontrar la tarea o la interrupcion,
	 *             genera una excepcion
	 */
	@Loc(size = 7, taskId = 73)
	void adicionarInterrupcionATarea(int idInterrupcion, int idTarea,
			int minutos, String fecha) throws Exception {
		Tarea tarea = this.encontrarTarea(idTarea);
		if (tarea == null)
			throw new Exception("Tarea no encontrada. ID=" + idTarea);

		Interrupcion interrupcion = this.encontrarInterrupcion(idInterrupcion);
		if (interrupcion == null)
			throw new Exception("Interrupcion NO encontrada. ID="
					+ idInterrupcion);

		tarea.adicionarInterrupciones(interrupcion, minutos);
	}

	/**
	 * Agrega un defecto encontrado en una tarea
	 * 
	 * @param etapaInyectada
	 *            Etapa en la que fue inyectado este defecto
	 * @param idDefecto
	 *            Id del defecto encontrado
	 * @param idTarea
	 *            Id de la tarea encontrada
	 * @throws Exception
	 *             Arojja una excepcion si no encuentra el defecto o la tarea
	 */
	@Loc(size = 7, taskId = 73)
	void adicionarDefectoATarea(Etapa etapaInyectada, int idDefecto, int idTarea)
			throws Exception {
		Tarea tarea = this.encontrarTarea(idTarea);
		if (tarea == null)
			throw new Exception("Tarea no encontrada. ID=" + idTarea);

		Defecto defecto = this.encontrarDefecto(idDefecto);
		if (defecto == null)
			throw new Exception("Defecto NO encontrado. ID=" + idDefecto);

		tarea.adicionarDefecto(defecto, etapaInyectada);
	}

	/**
	 * Da por terminada una tarea
	 * 
	 * @param idTarea
	 *            ID de la tarea a terminar
	 * @throws Exception
	 *             Arroja excepcion si no encuentra la tarea
	 */
	@Loc(size = 4, taskId = 73)
	void terminarTarea(int idTarea) throws Exception {
		Tarea tarea = this.encontrarTarea(idTarea);
		if (tarea == null)
			throw new Exception("Tarea no encontrada. ID=" + idTarea);

		tarea.terminar();
	}

	/**
	 * Agrega un error de carga de archivo
	 * 
	 * @param error
	 *            Error a ser cargado
	 */
	void agregarErrorDeCarga(String error) {
		this.erroresDuranteCarga.add(error);
	}

	/**
	 * Procesa un archivo de Java
	 * @param nombreArchivo Ruta absoluta del archivo a procesar
	 * @param proyectoATrabajar Proyecto al cual se le van a cargar los datos a ser procesados en el archivo de java
	 * @return Lista de errores encontrados en el cargue
	 * @throws Exception Puede arrojar una excepcion si occure
	 */
	@Loc(size = 3, taskId = 72)
	public List<String> procesarArchivo(String nombreArchivo, Proyecto proyectoATrabajar) throws Exception 
	{
		for(Ciclo ciclo: proyectoATrabajar.listarCiclos())
		{
			for(Tarea tarea: ciclo.listarTareas()) // se limpian los datos reales, para cargarlos de nuevo en este proceso
			{
				tarea.reestablecerEnCerosParaRecalcular();
			}
		}
		
		this.erroresDuranteCarga = new ArrayList<String>();
		this.procesarArchivoJava(nombreArchivo);

		return this.erroresDuranteCarga;
	}

	/**
	 * Procesa un archivo Java
	 * 
	 * @param archivo
	 *            PAth absoluto del archivo a procesar
	 */
	@Loc(size = 1, taskId = 72)
	private void procesarArchivoJava(String archivo) {
		new Analizador(this).analizarArchivo(archivo);
	}

	/**
	 * Procesa un directorio y sus subdirectorios y archivos
	 * @param nombreDirectorio Ruta absoluta del directorio a ser procesado
	 * @param proyectoATrabajar Proyecto en donde se planea hacer el cargue
	 * @return Lista de errores encontrados en el cargue
	 */
	@Loc(size = 3, taskId = 72)
	@LogD(etapaInyectada=Etapa.DISENO, idDefecto=4, taskId=962) // no se tenia en cuenta que se deben vaciar los datos del proyecto antes de recargar
	public List<String> procesarDirectorio(String nombreDirectorio, Proyecto proyectoATrabajar) 
	{
		for(Ciclo ciclo: proyectoATrabajar.listarCiclos())
		{
			for(Tarea tarea: ciclo.listarTareas()) // se limpian los datos reales, para cargarlos de nuevo en este proceso
			{
				tarea.reestablecerEnCerosParaRecalcular();
			}
		}
		
		this.erroresDuranteCarga = new ArrayList<String>();
		this.procesarDirectorioJava(nombreDirectorio);

		return this.erroresDuranteCarga;
	}

	/**
	 * Carga de directorios y archios internos
	 * 
	 * @param nombreDirectorio
	 *            Nombre del directorio a procesar
	 */
	@Loc(size = 11, taskId = 72)
	private void procesarDirectorioJava(String nombreDirectorio) {
		File dir = new File(nombreDirectorio);

		for (File archivo : dir.listFiles()) {
			if (!archivo.isDirectory()) {
				if (archivo.getName().toLowerCase().endsWith(".java"))
					this.procesarArchivoJava(archivo.getAbsolutePath());
			} else
				this.procesarDirectorioJava(archivo.getAbsolutePath());
		}
	}

	/**
	 * Metodo para cargar el modelo perisitido a memoria principal y se
	 * consumido por otras entidades.
	 * 
	 * @throws Exception
	 */
	@Tiempos( { @LogT(date = "2011-03-20", min = 13, taskId = 58),
			@LogT(date = "2011-03-20", min = 18, taskId = 74) })
	@LogInt(date = "2011-03-20", intId = 4, min = 53, taskId = 74)
	@Loc(size = 37, taskId = 74)
	public void cargarModelo() throws Exception {

		try {
			DefectoDAL defectosAcargar = new DefectoDAL();
			this.defectos = defectosAcargar.deSerializar();
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al Cargar Defectos");
		}
		try {
			InterrupcionDAL interrupcionesAcargar = new InterrupcionDAL();
			this.interrupciones = interrupcionesAcargar.deSerializar();
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al Cargar Interrupciones");
		}
		try {
			ProyectoDAL proyectoAcargar = new ProyectoDAL();
			this.proyectos = proyectoAcargar.listar();
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al Cargar Proyectos");
		}
	}
	@Loc(size = 18, taskId = 82)
	public void agregarDesarrollador(Proyecto proyecto, String nombre,
			String codigo, Rol rol) throws NumberFormatException, Exception {

		if (rol != null) {
			try {
				List<Desarrollador> desarrolladores = rol
						.listarDesarrolladores();
				boolean repetido = false;
				for (Desarrollador buscado : desarrolladores) {

					if (buscado.getCodigo() == Integer.parseInt(codigo)) {
						repetido = true;
					}
				}
				if (!repetido) {
					Desarrollador desarrollador = new Desarrollador();

					desarrollador.setCodigo(Integer.parseInt(codigo));
					desarrollador.setNombre(nombre);
					rol.agregarDesarrollador(desarrollador);
				}
				else
					throw new Exception("El codigo del desarrollador ya existe.");

			} catch (NumberFormatException e) {
				throw new Exception(
						"El codigo del desarrollador debe ser numerico");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				throw new Exception("No es posible adicionarlo al listado");
			}

		} else {
			// TODO Auto-generated catch block
			throw new Exception("El rol seleccionado no existe");
		}

	}

	/**
	 * Alamacena el modelo en un archivo para persistirlo
	 * 
	 * @throws Exception
	 */
	@LogT(date = "2011-03-20", min = 8, taskId = 111)
	@Loc(size = 30, taskId = 111)
	public void almacenarModelo() throws Exception {
		try {
			DefectoDAL defectosAalmacenar = new DefectoDAL();
			defectosAalmacenar.serializar((ArrayList<Defecto>) this.defectos);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al serializar Defectos");
		}
		try {
			InterrupcionDAL interrupcionesAalmacenar = new InterrupcionDAL();
			interrupcionesAalmacenar
					.serializar((ArrayList<Interrupcion>) this.interrupciones);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al serializar Interrupciones");
		}
		try {
			ProyectoDAL proyectoAalmacenar = new ProyectoDAL();
			proyectoAalmacenar.almacenar(this.proyectos);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("Error al serializar Proyectos");
		}
	}
	/**
	 * Pide a {@link EntidadReporteCalidad} generar el reporte
	 * 
	 * @throws Exception
	 */
	@LogT(date = "2011-03-26", min = 5, taskId = 130)
	@Loc(size = 1, taskId = 130)
	public EntidadReporteCalidad generarReporteCalidad(Ciclo ciclo){
		EntidadReporteCalidad reporte = new EntidadReporteCalidad();
		return reporte.generarReporte(ciclo);
		
	}
	
	/**
	 * Pide a {@link EntidadReporteProducto} generar el reporte
	 * 
	 * @throws Exception
	 */
	@LogT(date = "2011-03-26", min = 5, taskId = 131)
	@Loc(size = 1, taskId = 131)
	public EntidadReporteProducto generarReporteProducto(Ciclo ciclo){
		EntidadReporteProducto reporteP = new EntidadReporteProducto(ciclo);
		return reporteP;		
	}
	
	/**
	 * Pide a {@link EntidadReporteProducto} generar el reporte
	 * 
	 * @throws Exception
	 */
	@LogT(date = "2011-03-28", min = 5, taskId = 131)
	@Loc(size = 1, taskId = 131)
	public EntidadReporteProducto generarReporteProducto(Proyecto proyecto){
		EntidadReporteProducto reporteP = new EntidadReporteProducto(proyecto);
		return reporteP;
	}


	/**
	 * Pide a {@link EntidadReporteAvance} generar el reporte
	 * 
	 * @throws Exception
	 */
	@LogT(date = "2011-03-27", min = 2, taskId = 129)
	@Loc(size = 1, taskId = 129)
	public EntidadReporteAvance generarReporteAvance(ArrayList<Proyecto> proyectos, int numCiclo, int codDesarrollador, String nomProyecto){
		EntidadReporteAvance reporte = new EntidadReporteAvance();
		return reporte.generarReporte(proyectos, numCiclo, codDesarrollador, nomProyecto);
		
	}

}
